/***************************************************************************
 *                             ScriptCompiler.cs
 *                            -------------------
 *   begin                : May 1, 2002
 *   copyright            : (C) The RunUO Software Team
 *   email                : info@runuo.com
 *
 *   $Id: ScriptCompiler.cs 258 2007-09-15 21:17:08Z mark $
 *
 ***************************************************************************/

/***************************************************************************
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 ***************************************************************************/

using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Reflection;
using System.Security.Cryptography;
using Microsoft.CSharp;
using Microsoft.VisualBasic;
using Server.Accounting;

namespace Server
{
	public class ScriptCompiler
	{
		public static bool InitAndConfig()
		{
			Console.Write("Scripts: Verifying...");
			Core.VerifySerialization();
			Console.WriteLine("done ({0} items, {1} mobiles)", Core.ScriptItems, Core.ScriptMobiles);

			List<MethodInfo> invoke = new List<MethodInfo>();

			Type[] types = Assembly.GetExecutingAssembly().GetTypes();

			for (int i = 0; i < types.Length; ++i)
			{
				MethodInfo m = types[i].GetMethod("Configure", BindingFlags.Static | BindingFlags.Public);

				if (m != null)
					invoke.Add(m);
			}

			invoke.Sort(new CallPriorityComparer());

			for (int i = 0; i < invoke.Count; ++i)
				invoke[i].Invoke(null, null);

			invoke.Clear();

			Region.Load();
			World.Load();

			types = Assembly.GetExecutingAssembly().GetTypes();

			for (int i = 0; i < types.Length; ++i)
			{
				MethodInfo m = types[i].GetMethod("Initialize", BindingFlags.Static | BindingFlags.Public);

				if (m != null)
					invoke.Add(m);
			}

			invoke.Sort(new CallPriorityComparer());

			for (int i = 0; i < invoke.Count; ++i)
				invoke[i].Invoke(null, null);

			return true;
		}

		private static Dictionary<Assembly, TypeCache> m_TypeCaches = new Dictionary<Assembly, TypeCache>();

		private static TypeCache m_NullCache;

		public static TypeCache GetTypeCache(Assembly asm)
		{
			if (asm == null)
			{
				if (m_NullCache == null)
					m_NullCache = new TypeCache(null);

				return m_NullCache;
			}

			TypeCache c = null;
			m_TypeCaches.TryGetValue(asm, out c);

			if (c == null)
				m_TypeCaches[asm] = c = new TypeCache(asm);

			return c;
		}

		public static Type FindTypeByFullName(string fullName)
		{
			return FindTypeByFullName(fullName, true);
		}

		public static Type FindTypeByFullName(string fullName, bool ignoreCase)
		{
			return GetTypeCache(Core.Assembly).GetTypeByFullName(fullName, ignoreCase);
		}

		public static Type FindTypeByName(string name)
		{
			return FindTypeByName(name, true);
		}

		public static Type FindTypeByName(string name, bool ignoreCase)
		{
			return GetTypeCache(Core.Assembly).GetTypeByName(name, ignoreCase);
		}

		public static void EnsureDirectory(string dir)
		{
			string path = Path.Combine(Core.BaseDirectory, dir);

			if (!Directory.Exists(path))
				Directory.CreateDirectory(path);
		}

		public static string[] GetScripts(string filter)
		{
			List<string> list = new List<string>();

			GetScripts(list, Path.Combine(Core.BaseDirectory, "Scripts"), filter);

			return list.ToArray();
		}

		public static void GetScripts(List<string> list, string path, string filter)
		{
			foreach (string dir in Directory.GetDirectories(path))
				GetScripts(list, dir, filter);

			list.AddRange(Directory.GetFiles(path, filter));
		}
	}

	public class TypeCache
	{
		private Type[] m_Types;
		private TypeTable m_Names, m_FullNames;

		public Type[] Types { get { return m_Types; } }
		public TypeTable Names { get { return m_Names; } }
		public TypeTable FullNames { get { return m_FullNames; } }

		public Type GetTypeByName(string name, bool ignoreCase)
		{
			return m_Names.Get(name, ignoreCase);
		}

		public Type GetTypeByFullName(string fullName, bool ignoreCase)
		{
			return m_FullNames.Get(fullName, ignoreCase);
		}

		public TypeCache(Assembly asm)
		{
			if (asm == null)
				m_Types = Type.EmptyTypes;
			else
				m_Types = asm.GetTypes();

			m_Names = new TypeTable(m_Types.Length);
			m_FullNames = new TypeTable(m_Types.Length);

			Type typeofTypeAliasAttribute = typeof(TypeAliasAttribute);

			for (int i = 0; i < m_Types.Length; ++i)
			{
				Type type = m_Types[i];

				m_Names.Add(type.Name, type);
				m_FullNames.Add(type.FullName, type);

				if (type.IsDefined(typeofTypeAliasAttribute, false))
				{
					object[] attrs = type.GetCustomAttributes(typeofTypeAliasAttribute, false);

					if (attrs != null && attrs.Length > 0)
					{
						TypeAliasAttribute attr = attrs[0] as TypeAliasAttribute;

						if (attr != null)
						{
							for (int j = 0; j < attr.Aliases.Length; ++j)
								m_FullNames.Add(attr.Aliases[j], type);
						}
					}
				}
			}
		}
	}

	public class TypeTable
	{
		private Dictionary<string, Type> m_Sensitive, m_Insensitive;

		public void Add(string key, Type type)
		{
			m_Sensitive[key] = type;
			m_Insensitive[key] = type;
		}

		public Type Get(string key, bool ignoreCase)
		{
			Type t = null;

			if (ignoreCase)
				m_Insensitive.TryGetValue(key, out t);
			else
				m_Sensitive.TryGetValue(key, out t);

			return t;
		}

		public TypeTable(int capacity)
		{
			m_Sensitive = new Dictionary<string, Type>(capacity);
			m_Insensitive = new Dictionary<string, Type>(capacity, StringComparer.OrdinalIgnoreCase);
		}
	}
}
